home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / streaming / qtfiletransfer / qtfiletransfer.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  10.1 KB  |  324 lines

  1. //////////
  2. //
  3. //    File:        QTFileTransfer.c
  4. //
  5. //    Contains:    Sample code for transferring a file asynchronously from a web server.
  6. //
  7. //    Written by:    Tim Monroe
  8. //
  9. //    Copyright:    © 1998-1999 by Apple Computer, Inc., all rights reserved.
  10. //
  11. //    Change History (most recent first):
  12. //
  13. //       <5>         03/03/99    rtm        switched to using DataHReadAsync; removed unnecessary NOTES
  14. //       <4>         12/25/99    rtm        added NOTES concerning DataHReadAsync and DataHGetFileSize
  15. //       <3>         11/30/98    rtm        modified code to use GetDataHandler instead of FindNextComponent;
  16. //                                    moved call to QTFileTrans_CloseDownHandlers out of completion proc
  17. //       <2>         11/16/98    rtm        got asynchronous ftp and http file transfer working
  18. //       <1>         11/11/98    rtm        first file
  19. //     
  20. //    QuickTime Streaming has ftp and http data handlers, which you can use to transfer files
  21. //    synchronously or asynchronously from a web server. This sample code illustrates how to
  22. //    perform asynchronous transfers. In all likelihood, you'll want to transfer asynchronously,
  23. //    since your application can continue to operate while the transfer is underway.
  24. //    
  25. //    The basic idea is to instantiate the URL data handler and the HFS data handler; the URL
  26. //    data handler will be reading data from a remote ftp or http file into a buffer, and the
  27. //    HFS data handler will be writing data from that buffer into a local file. This reading and
  28. //    writing continues until the file is completely transferred.
  29. //
  30. //    To transfer a remote file to the local machine, call the QTFileTrans_CopyRemoteFileToLocalFile
  31. //    function defined here. It does all the necessary set-up and schedules the first read request;
  32. //    all subsequent write and read requests are scheduled by the read and write completion routines.
  33. //    Note that, when doing asynchronous transfers, you need to give time to the data handlers by
  34. //    calling DataHTask periodically; on the Mac, you can put code like this in your main event loop:
  35. //
  36. //        // if we're done, close down the data handlers
  37. //        if (gDoneTransferring)
  38. //            QTFileTrans_CloseDownHandlers();
  39. //
  40. //        // give the data handlers some time, if they are still active
  41. //        if (gDataReader != NULL)
  42. //            DataHTask(gDataReader);
  43. //        
  44. //        if (gDataWriter != NULL)
  45. //            DataHTask(gDataWriter);
  46. //    
  47. //    On Windows, you could install a timer that calls this code at a specified interval. (On either
  48. //    platform, you should probably also implement some way of making sure that the user doesn't quit
  49. //    the application while a transfer is underway.)
  50. //
  51. //    NOTES:
  52. //
  53. //    *** (1) ***
  54. //    For information about the main routines used here, see the chapter "Data Handler Components" in
  55. //    the document QT3.0Reference.pdf.
  56. //
  57. //    *** (2) ***
  58. //    The code for implementing synchronous transfers is actually much simpler: you don't need any
  59. //    completion routines, and the "scheduling" is much easier. Here's an outline of all you need to do
  60. //    to transfer a file synchronously:
  61. //
  62. //        DataHGetData(gDataReader, myDataBuffer, 0L, 0L, kDataBufferSize);                    
  63. //        DataHCloseForRead(gDataReader);
  64. //        DataHPutData(gDataWriter, myDataBuffer, 0L, NULL, kDataBufferSize);
  65. //        DataHCloseForWrite(gDataWriter);    
  66. //
  67. //    In this case, however, myDataBuffer is a handle, not a pointer. (Also, we've assumed that the file
  68. //    being transferred fits completely into the buffer; you could easily fix that assumption.)
  69. //
  70. //    *** (3) ***
  71. //    You'll notice that we use our completion routines to schedule subsequent data reads and writes.
  72. //    This is okay because data handler completion routines are never called at interrupt time.
  73. //
  74. //    *** (4) ***
  75. //    In some instances, DataHGetFileSize is not able to determine the size of the file to be downloaded
  76. //    (for example, an FTP server might not support the SIZE command). A more general strategy therefore
  77. //    would be to download a file until you get eofErr. Implementing this strategy is left as an exercise
  78. //    for the reader.
  79. //
  80. //////////
  81.  
  82.  
  83. #include "QTFileTransfer.h"
  84.  
  85. //global variables
  86. ComponentInstance                gDataReader = NULL;            // the data handler that reads data from the URL
  87. ComponentInstance                gDataWriter = NULL;            // the data handler that writes data to an HFS file
  88. DataHCompletionUPP                gReadDataHCompletionUPP = NULL;
  89. DataHCompletionUPP                gWriteDataHCompletionUPP = NULL;
  90. long                            gBytesToTransfer = 0L;        // the number of bytes to transfer
  91. long                            gBytesTransferred = 0L;        // the number of bytes already transferred
  92. Boolean                            gDoneTransferring = false;    // are we done transferring data?
  93.  
  94.  
  95. //////////
  96. //
  97. // QTFileTrans_CopyRemoteFileToLocalFile
  98. // Copy a remote file (located at the specified URL) into a local file.
  99. //
  100. //////////
  101.  
  102. OSErr QTFileTrans_CopyRemoteFileToLocalFile (char *theURL, FSSpecPtr theFSSpecPtr)
  103. {
  104.     Handle                        myReaderRef = NULL;            // data reference for the remote file
  105.     Handle                        myWriterRef = NULL;            // data reference for the local file
  106.     Ptr                            myDataBuffer = NULL;        // buffer that holds data being transferred
  107.     Size                        mySize = 0;
  108.     ComponentResult                myErr = badComponentType;
  109.  
  110.     //////////
  111.     //
  112.     // create a data reference for the remote file
  113.     //
  114.     //////////
  115.     
  116.     // get the size of the URL, plus the terminating null byte
  117.     mySize = (Size)strlen(theURL) + 1;
  118.     if (mySize == 0)
  119.         goto bail;
  120.     
  121.     // allocate a new handle
  122.     myReaderRef = NewHandleClear(mySize);
  123.     if (myReaderRef == NULL)
  124.         goto bail;
  125.  
  126.     // copy the URL into the handle
  127.     BlockMove(theURL, *myReaderRef, mySize);
  128.  
  129.     //////////
  130.     //
  131.     // create a data reference for the local file
  132.     //
  133.     //////////
  134.     
  135.     // delete the target local file, if it already exists;
  136.     // if it doesn't exist yet, we'll get an error (fnfErr), which we just ignore
  137.     FSpDelete(theFSSpecPtr);
  138.     
  139.     myWriterRef = NewHandleClear(sizeof(Handle));
  140.     if (myWriterRef == NULL)
  141.         goto bail;
  142.  
  143.     // create the local file
  144.     myErr = FSpCreate(theFSSpecPtr, kTransFileCreator, kTransFileType, smSystemScript);
  145.     if (myErr != noErr)
  146.         goto bail;
  147.  
  148.     myErr = QTNewAlias(theFSSpecPtr, (AliasHandle *)&myWriterRef, true);
  149.     if (myErr != noErr)
  150.         goto bail;
  151.  
  152.     //////////
  153.     //
  154.     // find and open the Apple URL and HFS data handlers; connect the data references to them
  155.     //
  156.     //////////
  157.     
  158.     gDataReader = OpenComponent(GetDataHandler(myReaderRef, URLDataHandlerSubType, kDataHCanRead));
  159.     if (gDataReader == NULL)
  160.         goto bail;
  161.  
  162.     gDataWriter = OpenComponent(GetDataHandler(myWriterRef, rAliasType, kDataHCanWrite));
  163.     if (gDataWriter == NULL)
  164.         goto bail;
  165.         
  166.     // set the data reference for the URL data handler
  167.     myErr = DataHSetDataRef(gDataReader, myReaderRef);
  168.     if (myErr != noErr)
  169.         goto bail;
  170.     
  171.     // set the data reference for the HFS data handler
  172.     myErr = DataHSetDataRef(gDataWriter, myWriterRef);
  173.     if (myErr != noErr)
  174.         goto bail;
  175.     
  176.     //////////
  177.     //
  178.     // allocate a data buffer; the URL data handler copies data into this buffer,
  179.     // and the HFS data handler copies data out of it
  180.     //
  181.     //////////
  182.     
  183.     myDataBuffer = NewPtrClear(kDataBufferSize);
  184.     myErr = MemError();
  185.     if (myErr != noErr)
  186.         goto bail;
  187.         
  188.     //////////
  189.     //
  190.     // connect to the remote and local files
  191.     //
  192.     //////////
  193.     
  194.     // open a read-only path to the remote data reference
  195.     myErr = DataHOpenForRead(gDataReader);
  196.     if (myErr != noErr)
  197.         goto bail;
  198.  
  199.     // get the size of the remote file
  200.     myErr = DataHGetFileSize(gDataReader, &gBytesToTransfer); 
  201.     if (myErr != noErr)
  202.         goto bail;
  203.     
  204.     // open a write-only path to the local data reference
  205.     myErr = DataHOpenForWrite(gDataWriter);
  206.     if (myErr != noErr)
  207.         goto bail;
  208.         
  209.     //////////
  210.     //
  211.     // start reading and writing data
  212.     //
  213.     //////////
  214.     
  215.     gDoneTransferring = false;
  216.     gBytesTransferred = 0L;
  217.     
  218.     gReadDataHCompletionUPP = NewDataHCompletionProc(QTFileTrans_ReadDataCompletionProc);
  219.     gWriteDataHCompletionUPP = NewDataHCompletionProc(QTFileTrans_WriteDataCompletionProc);
  220.         
  221.     // start retrieving the data; we do this by calling our own write completion routine,
  222.     // pretending that we've just successfully finished writing 0 bytes of data
  223.     QTFileTrans_WriteDataCompletionProc(myDataBuffer, 0L, noErr);
  224.  
  225. bail:
  226.     // if we encountered any error, close the data handler components
  227.     if (myErr != noErr)
  228.         QTFileTrans_CloseDownHandlers();
  229.     
  230.     return((OSErr)myErr);
  231. }
  232.  
  233.  
  234. //////////
  235. //
  236. // QTFileTrans_ReadDataCompletionProc
  237. // This procedure is called when the data handler has completed a read operation.
  238. //
  239. // The theRefCon parameter contains the number of bytes just read.
  240. //
  241. //////////
  242.  
  243. void QTFileTrans_ReadDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr)
  244. {
  245. #pragma unused(theErr)
  246.  
  247.     // we just finished reading some data, so schedule a write operation            
  248.     DataHWrite(    gDataWriter,
  249.                 theRequest,                        // the data buffer
  250.                 gBytesTransferred,                // write from the current offset
  251.                 theRefCon,                        // the number of bytes to write
  252.                 gWriteDataHCompletionUPP,
  253.                 theRefCon);
  254. }
  255.  
  256.  
  257. //////////
  258. //
  259. // QTFileTrans_WriteDataCompletionProc
  260. // This procedure is called when the data handler has completed a write operation.
  261. //
  262. // The theRefCon parameter contains the number of bytes just written.
  263. //
  264. //////////
  265.  
  266. void QTFileTrans_WriteDataCompletionProc (Ptr theRequest, long theRefCon, OSErr theErr)
  267. {
  268. #pragma unused(theErr)
  269.  
  270.     long        myNumBytesToRead;
  271.     wide        myWide;
  272.  
  273.     // increment our tally of the number of bytes written so far
  274.     gBytesTransferred += theRefCon;
  275.  
  276.     if (gBytesTransferred < gBytesToTransfer) {
  277.         // there is still data to read and write, so schedule a read operation
  278.     
  279.         // determine how big a chunk to read
  280.         if (gBytesToTransfer - gBytesTransferred > kDataBufferSize)
  281.             myNumBytesToRead = kDataBufferSize;
  282.         else
  283.             myNumBytesToRead = gBytesToTransfer - gBytesTransferred;
  284.  
  285.         myWide.lo = gBytesTransferred;            // read from the current offset 
  286.         myWide.hi = 0;
  287.         
  288.         // schedule a read operation
  289.         DataHReadAsync(gDataReader,
  290.                         theRequest,                // the data buffer
  291.                         myNumBytesToRead,
  292.                         &myWide,
  293.                         gReadDataHCompletionUPP,
  294.                         myNumBytesToRead);
  295.  
  296.     } else {
  297.         // we've transferred all the data, so set a flag to tell us to close down the data handlers
  298.         gDoneTransferring = true;
  299.     }
  300.     
  301. }
  302.  
  303.  
  304. //////////
  305. //
  306. // QTFileTrans_CloseDownHandlers
  307. // Close our read/write access to our data references and then close down the read/write data handlers.
  308. //
  309. //////////
  310.  
  311. void QTFileTrans_CloseDownHandlers (void)
  312. {
  313.     if (gDataReader != NULL) {
  314.         DataHCloseForRead(gDataReader);
  315.         CloseComponent(gDataReader);
  316.         gDataReader = NULL;
  317.     }
  318.  
  319.     if (gDataWriter != NULL) {
  320.         DataHCloseForWrite(gDataWriter);
  321.         CloseComponent(gDataWriter);
  322.         gDataWriter = NULL;
  323.     }
  324. }